/************************************************************************/
/*									*/
/*  This is the CMEM_RCC driver	 					*/
/*  Its purpose is to provide user applications with contiguous data 	*/
/*  buffers for DMA operations. 					*/
/*									*/
/*  12. Dec. 01  MAJO  created						*/
/*									*/
/*******C 2019 - The software with that certain something****************/

/************************************************************************/
/*NOTES:								*/
/*- This driver should work on kernels from 2.6.9 onwards		*/
/************************************************************************/

/*
How to make cmem_rcc_numa:
1) mv cmem_rcc_numa.c cmem_rcc_numa.c_old
2) cp cmem_rcc.c cmem_rcc_numa.c
3) nedit cmem_rcc_numa.c
4) replace (case sensitive) "cmem_rcc" by "cmem_rcc_numa"
5) replace "#include "cmem_rcc_numa/cmem_rcc_numa_drv.h"" by "#include "cmem_rcc/cmem_rcc_drv.h""
6) replace "cmem_rcc_numa_t" by "cmem_rcc_t"
7) look for "//MJ?? set numa_id to 1 for second driver" and set numa_id to 1
*/




//Note: 20.2.2023 The individual spinlocks in the ioctl function have been replaced by one global lock. This was necessary because the driver had a race condition
//                (spotted by LAr in an ALTI system). Identifying the reason for the race condition would potentially have been time consuming. A global lock
//                for ioctl should not have a noticeable performance penalty.

//Note: On a computer with more than one NUMA zone it is not possible to allocate a very large buffer across NUMA zones. The maximum size of gfpbpa_size is the size of a single NUMA zone
//Note: The number of NUMA nodes available on a system can be found in /sys/devices/system/node

//Note: comments like "MJ: p56" refer to the respective page in the 3rd edition of "Linux device drivers"


#include <linux/init.h>           //MJ: for 2.6, p30
#include <linux/module.h>
#include <linux/moduleparam.h>    //MJ: for 2.6, p30
#include <linux/kernel.h>
#include <linux/stat.h>           //MJ: for 2.6, e.g. for module_param
#include <linux/fs.h>
#include <linux/sched.h>          //MJ: for current->pid (first needed with SLC6)
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/cdev.h>           //e.g. for cdev_alloc
#include <linux/proc_fs.h>
#include <linux/version.h>
#include <linux/seq_file.h>       //CS9
#include <linux/semaphore.h>

#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)
  #include <asm/uaccess.h>
#else
  #include <linux/uaccess.h>        //for CS8
#endif

#include <asm/io.h>
#include <asm/page.h>
#include "cmem_rcc/cmem_rcc_drv.h"
#include "wupper/tdaq_drivers.h"

#define MAX_GFPBPA_SIZE (256 * 1024)  //256 GB
#define MAX_NUMA_ZONES  4

// Globals
static int gfpbpainit_level, debug = 0, errorlog = 1, gfpbpa_zone = 0, virtual_zones = 1, gfpbpa_quantum = 4, numa_zones = 1, number_zones, type_zones;
static char *proc_read_text;
static cmem_rcc_t *buffer_table; 
static u_long gfpbpa_base[MAX_NUMA_ZONES], gfpbpa_size = 0, gfpbpa[MAX_NUMA_ZONES], gfpbpa_array[MAX_NUMA_ZONES][MAX_GFPBPA_SIZE], gfpbpa_num_pages[MAX_NUMA_ZONES];
static u_int gfpbpa_order;
static dev_t major_minor;
static struct cdev *cmem_rcc_cdev_remap_pfn_range, *cmem_rcc_cdev_nopage;
static range_t *gfpbpafree_list[MAX_NUMA_ZONES];
static range_t *gfpbpaused_list[MAX_NUMA_ZONES];

#if LINUX_VERSION_CODE < KERNEL_VERSION(6,4,0)
static DEFINE_SEMAPHORE(semlock);
#else
static DEFINE_SEMAPHORE(semlock, 1);
#endif

/******************************/
/*Standard function prototypes*/
/******************************/
static int cmem_rcc_open(struct inode *inode, struct file *file);
static int cmem_rcc_release(struct inode *inode, struct file *file);
static long cmem_rcc_ioctl(struct file *file, u_int cmd, u_long arg);
static int cmem_rcc_mmap_remap_pfn_range(struct file *file, struct vm_area_struct *vma);
static int cmem_rcc_mmap_nopage(struct file *filp, struct vm_area_struct *vma);
static ssize_t cmem_rcc_proc_write(struct file *file, const char *buffer, size_t count, loff_t *startOffset);
int cmem_rcc_proc_open(struct inode *inode, struct file *file);
int cmem_rcc_proc_show(struct seq_file *sfile, void *p);

/*****************************/
/*Service function prototypes*/
/*****************************/
static int membpa_init2(int priority, u_int btype);
static int gfpbpa_init(void);
static void* membpa_alloc_pages(int count, int align, int priority, u_int btype, u_int numaid);
static int membpa_free_pages(void *base, u_int btype, u_int numaid);
static void cmem_rcc_vmaClose(struct vm_area_struct *vma);
static void cmem_rcc_vmaOpen(struct vm_area_struct *vma);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0) ///MJ: The "4,17,0" was inspired by https://github.com/martinezjavier/ldd3/blob/master/simple/simple.c
  static int cmem_rcc_nopage(struct vm_area_struct *vma, struct vm_fault *vmf);
#else
  static vm_fault_t cmem_rcc_nopage(struct vm_fault *vmf);
#endif


/***************************************************************/
/* Use /sbin/modinfo <module name> to extract this information */
/***************************************************************/
module_param (debug, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
MODULE_PARM_DESC(debug, "1 = enable debugging   0 = disable debugging");

module_param (errorlog, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
MODULE_PARM_DESC(errorlog, "1 = enable error logging   0 = disable error logging");

module_param (gfpbpa_size, long, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
MODULE_PARM_DESC(gfpbpa_size, "The amount of RAM in MB that will be pre-allocated");

module_param (gfpbpa_quantum, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
MODULE_PARM_DESC(gfpbpa_quantum, "The size (in MB) of a page used for the pre-allocation");

module_param (gfpbpa_zone, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
MODULE_PARM_DESC(gfpbpa_zone, "0: Anywhere in the RAM,  1: In the 32 bit address range");

module_param (virtual_zones, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
MODULE_PARM_DESC(virtual_zones, "Secret number of vitual zones");

module_param (numa_zones, int, S_IRUGO | S_IWUSR);  //MJ: for 2.6 p37
MODULE_PARM_DESC(numa_zones, "The number of NUMA IDs for which buffers are to be pre-allocated");

MODULE_DESCRIPTION("Allocation of contiguous memory");
MODULE_AUTHOR("Markus Joos, CERN/EP");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION("Felix 4.14 / TDAQ 10");

//CS9
//MJ: inspired by https://stackoverflow.com/questions/64931555/how-to-fix-error-passing-argument-4-of-proc-create-from-incompatible-pointer
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)  
static struct proc_ops cmem_rcc_proc_file_ops =
{
  .proc_open   = cmem_rcc_proc_open,
  .proc_write  = cmem_rcc_proc_write,     
  .proc_read   = seq_read,
  .proc_lseek  = seq_lseek	

};
#else
static struct file_operations cmem_rcc_proc_file_ops = 
{
  .owner   = THIS_MODULE,
  .open    = cmem_rcc_proc_open,
  .write   = cmem_rcc_proc_write,
  .read    = seq_read,
  .llseek  = seq_lseek,
  .release = single_release
};
#endif

// The ordinary device operations
// Device 0 uses remap_pfn_range
static struct file_operations fops_remap_pfn_range =
{
  .owner          = THIS_MODULE,
  .unlocked_ioctl = cmem_rcc_ioctl,
  .open           = cmem_rcc_open,
  .mmap           = cmem_rcc_mmap_remap_pfn_range,
  .release        = cmem_rcc_release,
};

// Device 1 uses nopage
static struct file_operations fops_nopage =
{
  .owner          = THIS_MODULE,
  .unlocked_ioctl = cmem_rcc_ioctl,
  .open           = cmem_rcc_open,
  .mmap           = cmem_rcc_mmap_nopage,
  .release        = cmem_rcc_release,
};


// memory handler functions.  
static struct vm_operations_struct cmem_rcc_vm_ops =
{
  .close = cmem_rcc_vmaClose,       //MJ: Not actually required. Just for kdebug
  .open  = cmem_rcc_vmaOpen,        //MJ: Not actually required. Just for kdebug
  .fault = cmem_rcc_nopage,         //JV: cmem_rcc_nopage essential for nopage method, MJ: Note the comma at the end of the list!
};

	
/****************************/
static int cmem_rcc_init(void)
/****************************/
{
  int ret, loop, numaloop, ecode = 0;
  static struct proc_dir_entry *cmem_rcc_file;

  if(gfpbpa_size)
  {
    ret = gfpbpa_init();
    if (ret == 1)
    {
      ecode = -ENOMEM;
      goto fail2;
    }
    gfpbpainit_level = 1;
  }
  else
    kdebug(("cmem_rcc(cmem_rcc_init): Pre-allocation of memory was not requested\n"));

  ecode = alloc_chrdev_region(&major_minor, 0, 2, "cmem_rcc"); //MJ: for 2.6 p45
  if (ecode)
  {
    kerror(("cmem_rcc(cmem_rcc_init): ERROR: failed to obtain device numbers\n"));
    goto fail3;
  }

  proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL);
  if (proc_read_text == NULL)
  {
    ecode = -ENOMEM;
    kerror(("cmem_rcc(cmem_rcc_init): ERROR: error from kmalloc\n"));
    goto fail4;
  }

  // Install /proc entry
  cmem_rcc_file = proc_create("cmem_rcc", 0644, NULL, &cmem_rcc_proc_file_ops);
  if (cmem_rcc_file == NULL)
  {
    kerror(("cmem_rcc(cmem_rcc_init): ERROR: error from call to create_proc_entry\n"));
    ecode = -EFAULT;
    goto fail5;
  }

  // Allocate memory for the buffer table
  kdebug(("cmem_rcc(cmem_rcc_init): MAX_BUFFS        = %d\n", MAX_BUFFS));
  kdebug(("cmem_rcc(cmem_rcc_init): sizeof(cmem_rcc_t) = %lu\n", (u_long)sizeof(cmem_rcc_t)));
  kdebug(("cmem_rcc(cmem_rcc_init): need %ld bytes\n", MAX_BUFFS * (u_long)sizeof(cmem_rcc_t)));
  buffer_table = (cmem_rcc_t *)kmalloc(MAX_BUFFS * sizeof(cmem_rcc_t), GFP_KERNEL);
  if (buffer_table == NULL)
  {
    kerror(("cmem_rcc(cmem_rcc_init): ERROR: unable to allocate memory for buffer table\n"));
    ecode = -EFAULT;
    goto fail6;
  }

  // Clear the buffer table
  for(loop = 0; loop < MAX_BUFFS; loop++)
  {
    buffer_table[loop].used   = 0;
    buffer_table[loop].paddr  = 0;
    buffer_table[loop].size   = 0;
    buffer_table[loop].locked = 0;
    buffer_table[loop].type   = 0;
    buffer_table[loop].pid    = 0;
  }

  cmem_rcc_cdev_remap_pfn_range = (struct cdev *)cdev_alloc();             //MJ: for 2.6 p55
  cdev_init(cmem_rcc_cdev_remap_pfn_range, &fops_remap_pfn_range);         //MJ: see p56/57
  cmem_rcc_cdev_remap_pfn_range->owner = THIS_MODULE;
  ecode = cdev_add(cmem_rcc_cdev_remap_pfn_range, major_minor, 1);         //MJ: for 2.6 p56
  if (ecode)
  {
    kerror(("cmem_rcc(cmem_rcc_init): ERROR: error from call to first cdev_add.\n"));
    goto fail7;
  }  
  
  cmem_rcc_cdev_nopage        = (struct cdev *)cdev_alloc();               //MJ: for 2.6 p55
  cdev_init(cmem_rcc_cdev_nopage, &fops_nopage);
  cmem_rcc_cdev_nopage->owner = THIS_MODULE;
  ecode = cdev_add(cmem_rcc_cdev_nopage, major_minor + 1, 1);              //MJ: for 2.6 p56
  if (ecode)
  {
    kerror(("cmem_rcc(cmem_rcc_init): ERROR: error from call to second cdev_add.\n"));
    goto fail7;
  }
  
  kdebug(("cmem_rcc(cmem_rcc_init): driver loaded; First node (remap_pfn_range): major device number = %d, minor = %d\n", MAJOR(major_minor), MINOR(major_minor)));
  kdebug(("cmem_rcc(cmem_rcc_init): driver loaded; Second node         (nopage): major device number = %d, minor = %d\n", MAJOR(major_minor + 1), MINOR(major_minor + 1)));
  return(0);

  fail7:
    kfree(buffer_table);

  fail6:
    remove_proc_entry("cmem_rcc", NULL);

  fail5:
    kfree(proc_read_text);

  fail4:
    unregister_chrdev_region(major_minor, 2); //MJ: for 2.6 p45

  fail3:
    if (gfpbpainit_level == 1)
    {      
      for(numaloop = 0; numaloop < numa_zones; numaloop++)
        for(loop = 0; loop < gfpbpa_num_pages[numaloop]; loop++)
          free_pages(gfpbpa_array[numaloop][loop], gfpbpa_order);
    }

  fail2:
    return(ecode);
}


/********************************/
static void cmem_rcc_cleanup(void)
/********************************/
{
  int numaloop, index, n;
  long npages, loop, chunk_size;
  u_long pfn_base;
  struct page *thepage;

  // Release orphaned buffers
  for(loop = 0; loop < MAX_BUFFS; loop++)
  {
    if (buffer_table[loop].used)
    {
      if (buffer_table[loop].locked)
      {
        kdebug(("cmem_rcc(cmem_rcc_cleanup): Releasing locked buffer: type=%d  paddr=0x%016lx  size=0x%016lx  name=%s\n", buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name));
      }
      else
      {
        kdebug(("cmem_rcc(cmem_rcc_cleanup): Releasing orphaned buffer: type=%d  paddr=0x%016lx  size=0x%016lx  name=%s\n", buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name));
      }

      if (buffer_table[loop].type == TYPE_GFP || buffer_table[loop].type == TYPE_NUMA)
      {
	free_pages(buffer_table[loop].kaddr, buffer_table[loop].order);    // free the area
      }
    }
  }

  kdebug(("cmem_rcc(cmem_rcc_cleanup): releasing gfp_bpa pages\n"));  
  npages = (gfpbpa_size * 1024 * 1024) / PAGE_SIZE;
  chunk_size = (gfpbpa_quantum * 1024 * 1024) / PAGE_SIZE;
  for(numaloop = 0; numaloop < numa_zones; numaloop++)
  {
    pfn_base = gfpbpa_base[numaloop] >> PAGE_SHIFT;
    kdebug(("cmem_rcc(cmem_rcc_cleanup): Free %ld pages starting at pfn 0x%lx\n", npages, pfn_base));
    for (loop = 0; loop < npages; loop++)
    {
      thepage = pfn_to_page(pfn_base + loop);
      index = loop / chunk_size;
      n = loop % chunk_size;
      ClearPageReserved(thepage);                         //This function will clear the PG_reserved bit (Jos is not sure if it really needs to be done). See: https://linux-kernel-labs.github.io/refs/heads/master/labs/memory_mapping.html and https://www.kernel.org/doc/gorman/html/understand/understand005.html
      free_page(gfpbpa_array[numaloop][index] + n * PAGE_SIZE);
    }
  }

  cdev_del(cmem_rcc_cdev_remap_pfn_range);            //MJ: for 2.6 p56
  cdev_del(cmem_rcc_cdev_nopage);                     //MJ: for 2.6 p56
  kdebug(("cmem_rcc(cmem_rcc_cleanup): character device deleted\n"));

  // Remove /proc entry
  remove_proc_entry("cmem_rcc", NULL);
  kfree(proc_read_text);
  kdebug(("cmem_rcc(cmem_rcc_cleanup): proc entry removed\n"));

  // Return the buffer table
  kfree(buffer_table);

  // Return the memory for the free lists(s)
  for(numaloop = 0; numaloop < numa_zones; numaloop++)
  {
    if (gfpbpafree_list[numaloop])
    {
      kdebug(("cmem_rcc(cmem_rcc_cleanup): freeing gfpbpafree_list[%d]\n",  numaloop));
      kfree(gfpbpafree_list[numaloop]);
    }
  }

  // Unregister the device
  unregister_chrdev_region(major_minor, 2); //MJ: for 2.6 p45
  kdebug(("cmem_rcc(cmem_rcc_cleanup): unregister_chrdev_region done\n"));

  kdebug(("cmem_rcc(cmem_rcc_cleanup): Done.\n"));
}


module_init(cmem_rcc_init);    //MJ: for 2.6 p16
module_exit(cmem_rcc_cleanup); //MJ: for 2.6 p16


/**************************************************************/
static int cmem_rcc_open(struct inode *inode, struct file *file)
/**************************************************************/
{
  int loop;
  private_stuff *pptr;

  kdebug(("cmem_rcc(cmem_rcc_open): function called for file at 0x%016lx\n", (u_long)file))

  //reserve space to store information about the memory buffers managed by this "file"
  pptr = (private_stuff *)kmalloc(sizeof(private_stuff), GFP_KERNEL);
  if (pptr == NULL)
  {
    kerror(("cmem_rcc(cmem_rcc_open): ERROR: error from kmalloc\n"));
    return(-EFAULT);
  }

  //Initialize the space
  for (loop = 0; loop < MAX_BUFFS; loop++)
    pptr->buffer[loop] = 0;

  file->private_data = pptr;
  kdebug(("cmem_rcc(cmem_rcc_open): private_data = 0x%016lx\n", (u_long)file->private_data));

  return(0);
}


/************************************************************/
int cmem_rcc_proc_open(struct inode *inode, struct file *file) 
/************************************************************/
{
  return single_open(file, cmem_rcc_proc_show, NULL);
}


/*****************************************************************/
static int cmem_rcc_release(struct inode *inode, struct file *file)
/*****************************************************************/
{
  int loop, ret;
  private_stuff *pptr;

  kdebug(("cmem_rcc(cmem_rcc_release): function called from process %d for file at 0x%016lx\n", current->pid, (u_long)file));
  pptr = (private_stuff *) file->private_data;

  // Release orphaned buffers of the current process

  if (down_interruptible(&semlock))
  {  
    kerror(("cmem_rcc(cmem_rcc_release, %d): down_interruptible was interrupted\n", current->pid));
    return(-EINTR);
  }
  kdebug(("cmem_rcc(cmem_rcc_release): semaphore taken\n"));
  
  for(loop = 0; loop < MAX_BUFFS; loop++)
  {
    if ((pptr->buffer[loop] == 1) && (!(buffer_table[loop].locked == 1)))
    {
      if (buffer_table[loop].type == TYPE_GFP || buffer_table[loop].type == TYPE_NUMA)
      {
        kdebug(("cmem_rcc(cmem_rcc_release): GFP, Releasing buffer with kaddr = 0x%016lx, paddr = 0x%016lx, type = %d and numa_id = %d\n", buffer_table[loop].kaddr, buffer_table[loop].paddr, buffer_table[loop].type, buffer_table[loop].numa_id));
 	free_pages(buffer_table[loop].kaddr, buffer_table[loop].order);            // free the area
      }
      else //TYPE_GFPBPA and TYPE_NUMABPA
      {  
        //MJ: 8.3.2023
	//    It can happen (see CMEM_RCC_FREE) that a buffer is de-allocated by another process (than the one that has allocated it)
	//    When that happens, the allocating process still has that buffer listed in its pptr->buffer[loop]
	//    We must, in this case, not de-allocate the buffer again
	if (buffer_table[loop].paddr == 0)
	{  
	  kdebug(("cmem_rcc(cmem_rcc_release, %d): Is seems that the buffer was already deallocated by another process\n", current->pid));
	  kdebug(("cmem_rcc(cmem_rcc_release): buffer_table[loop].paddr = 0x%016lx\n", buffer_table[loop].paddr));
	  kdebug(("cmem_rcc(cmem_rcc_release): buffer_table[loop].kaddr = 0x%016lx\n", buffer_table[loop].kaddr));
	  kdebug(("cmem_rcc(cmem_rcc_release): buffer_table[loop].size  = 0x%016lx\n", buffer_table[loop].size));
	  kdebug(("cmem_rcc(cmem_rcc_release): buffer_table[loop].type  = %d\n", buffer_table[loop].type));
	  kdebug(("cmem_rcc(cmem_rcc_release): buffer_table[loop].pid   = %d\n", buffer_table[loop].pid));
	  kdebug(("cmem_rcc(cmem_rcc_release): buffer_table[loop].order = %d\n", buffer_table[loop].order));
	  kdebug(("cmem_rcc(cmem_rcc_release): buffer_table[loop].used  = %d\n", buffer_table[loop].used));
	}
	else
	{
          kdebug(("cmem_rcc(cmem_rcc_release): GFPBPA, Calling membpa_free_pages with kaddr = 0x%016lx, paddr = 0x%016lx, type = %d and numa_id = %d\n", buffer_table[loop].kaddr, buffer_table[loop].paddr, buffer_table[loop].type, buffer_table[loop].numa_id));
          ret = membpa_free_pages((void *)buffer_table[loop].kaddr, buffer_table[loop].type, buffer_table[loop].numa_id);
	  if (ret)
	  {
  	    kerror(("cmem_rcc(cmem_rcc_release): ERROR: Error code %d received from membpa_free_pages\n", ret));
	  }
	  
	  kdebug(("cmem_rcc(cmem_rcc_release): Releasing orphaned buffer of process %d: type=%d  paddr=0x%016lx  size=0x%016lx  name=%s\n", buffer_table[loop].pid, buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name));
        }
      }

      // clear the entry in the buffer table
      buffer_table[loop].paddr = 0;
      buffer_table[loop].kaddr = 0;
      buffer_table[loop].size  = 0;
      buffer_table[loop].type  = 0;
      buffer_table[loop].pid   = 0;
      buffer_table[loop].order = 0;
      buffer_table[loop].used  = 0;
      pptr->buffer[loop] = 0;
    }
  }
  
  up(&semlock);
  kdebug(("cmem_rcc(cmem_rcc_release): semaphore released\n"))
  
  kfree(pptr);
  return(0);
}


/******************************************************************/
static long cmem_rcc_ioctl(struct file *file, u_int cmd, u_long arg)
/******************************************************************/
{
  private_stuff *pptr;

  int cpid;

  cpid = current->pid;
  
  if (down_interruptible(&semlock))
  {  
    kerror(("cmem_rcc(cmem_rcc_ioctl, %d): down_interruptible was interrupted\n", current->pid));
    return(-EINTR);
  }
  kdebug(("cmem_rcc(cmem_rcc_ioctl): semaphore taken\n"));

  kdebug(("cmem_rcc(ioctl, %d): cmd = %u (0x%08x)\n", cpid, cmd, cmd));
  pptr = (private_stuff *) file->private_data;

  switch (cmd)
  {
    case CMEM_RCC_GETNAME:
    {
      cmem_rcc_t uio_desc;
      int loop, namefound;

      if (copy_from_user(&uio_desc, (void *)arg, sizeof(cmem_rcc_t)) != 0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): ERROR: error in from copy_from_user\n", cpid));
        up(&semlock);
        kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME): semaphore released\n"))
        return(-CMEM_RCC_CFU);
      }
      
      // Look for a segment with the name provided by the user
      namefound = 0;
      for(loop = 0; loop < MAX_BUFFS; loop++)
      {
        if (strcmp(uio_desc.name, buffer_table[loop].name) == 0 && buffer_table[loop].used != 0)
        {
          kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): Segment with name %s found.\n", cpid, uio_desc.name));
          kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): buffer_table[%d].kaddr = 0x%016lx\n", cpid, loop, buffer_table[loop].kaddr));
          kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): buffer_table[%d].paddr = 0x%016lx\n", cpid, loop, buffer_table[loop].paddr));
          kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): buffer_table[%d].size  = 0x%016lx\n", cpid, loop, buffer_table[loop].size));
          kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): buffer_table[%d].name  = %s\n", cpid, loop, buffer_table[loop].name));
          kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): buffer_table[%d].order = 0x%08x\n", cpid, loop, (u_int)buffer_table[loop].order));
          kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): buffer_table[%d].type  = 0x%08x\n", cpid, loop, (u_int)buffer_table[loop].type));

          uio_desc.paddr   = buffer_table[loop].paddr;
          uio_desc.uaddr   = buffer_table[loop].uaddr;
          uio_desc.kaddr   = buffer_table[loop].kaddr;
          uio_desc.size    = buffer_table[loop].size;
          uio_desc.order   = buffer_table[loop].order;
          uio_desc.locked  = buffer_table[loop].locked;
          uio_desc.type    = buffer_table[loop].type;
          uio_desc.numa_id = buffer_table[loop].numa_id;
          uio_desc.handle  = loop;
          //strcpy(uio_desc.name, buffer_table[loop].name);
          namefound = 1;
          kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): Breaking loop\n", cpid));
          break;
        }
      }      

      if(namefound)
      {
        kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): copy_to_user\n", cpid));
        if (copy_to_user((void *)arg, &uio_desc, sizeof(cmem_rcc_t)) != 0)
        {
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): ERROR: error in from copy_to_user\n", cpid));
	  up(&semlock);
	  kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME): semaphore released\n"))
          return(-CMEM_RCC_CTU);
        }
      }
      else
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): ERROR: No buffer with the required name found.\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME): semaphore released\n"))
        return(-CMEM_RCC_ILLNAME);
      }

      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETNAME, %d): done\n", cpid));
      break;
    }

    case CMEM_RCC_GET:
    {
      u_int tnum, ok, pagecount;
      cmem_rcc_t uio_desc;
      struct page *page_ptr;
      void *numa_address;

      if (copy_from_user(&uio_desc, (void *)arg, sizeof(cmem_rcc_t)) !=0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): ERROR: error in from copy_from_user\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): semaphore released\n"))
        return(-CMEM_RCC_CFU);
      }
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): uio_desc.order = 0x%08x\n", cpid, uio_desc.order));
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): uio_desc.size = 0x%016lx\n", cpid, uio_desc.size));
      //Note: depending on the type of buffer either "order" or "size" is required. The other parameter is dummy
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): uio_desc.type = 0x%08x\n", cpid, uio_desc.type));
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): uio_desc.numa_id = 0x%08x\n", cpid, uio_desc.numa_id));

      // Look for a free slot in the buffer table
      ok = 0;
      for(tnum = 0; tnum < MAX_BUFFS; tnum++)
      {
        if (buffer_table[tnum].used == 0)
        {
          buffer_table[tnum].used = 1;  //This is to reserve the entry
          pptr->buffer[tnum] = 1;       //Remember which file this buffer will belong to
          uio_desc.handle = tnum;
          ok = 1;
          kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): tnum = %d\n", cpid, tnum));
          break;
        }
      }

      if (!ok)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): ERROR: all buffers are in use\n", cpid));
        up(&semlock);
        kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): semaphore released\n"))
        return(-CMEM_RCC_OVERFLOW);
      }

      if(uio_desc.type == TYPE_NUMA || uio_desc.type == TYPE_NUMABPA)
	if (uio_desc.numa_id >= max(numa_zones, virtual_zones) || uio_desc.numa_id < 0)
	{        
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET): ERROR: numa_id %d incorrect !!\n", uio_desc.numa_id));
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET): ERROR: uio_desc.numa_id = %d\n", uio_desc.numa_id));
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET): ERROR: numa_zones       = %d\n", numa_zones));
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET): ERROR: virtual_zones    = %d\n", virtual_zones));
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET): ERROR: uio_desc.type    = %d\n", uio_desc.type));
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET): ERROR: TYPE_NUMA        = %d\n", TYPE_NUMA));
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET): ERROR: TYPE_NUMABPA     = %d\n", TYPE_NUMABPA));
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET): ERROR: TYPE_GFP         = %d\n", TYPE_GFP));
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET): ERROR: TYPE_GFPBPA      = %d\n", TYPE_GFPBPA));
	  up(&semlock);
	  kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): semaphore released\n"))
          return(-1);
	}

      if(uio_desc.type == TYPE_NUMA || uio_desc.type == TYPE_GFP)
      {
	uio_desc.kaddr = 0;
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): calling alloc_pages(_node)\n", cpid));
	
	if(gfpbpa_zone)
	{
	  kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): allocating memory from DMA32 zone\n", cpid));
	  if(uio_desc.type == TYPE_NUMA)
	  {
    	    kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): Allocate below 4 GB. uio_desc.numa_id = %d, uio_desc.order = %d\n", cpid, uio_desc.numa_id, uio_desc.order));
	    page_ptr = alloc_pages_node(uio_desc.numa_id, GFP_DMA32, uio_desc.order);
	  }
	  else  	
	  {  
	    kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): Allocate below 4 GB. uio_desc.order = %d\n", cpid, uio_desc.order));
	    page_ptr = alloc_pages(GFP_DMA32, uio_desc.order);
	  }
	  if (!page_ptr)
	  {
            kerror(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): ERROR: alloc_pages_node failed\n", cpid));
            buffer_table[tnum].used = 0;  // No longer required
            pptr->buffer[tnum] = 0;
	    kerror(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): ERROR: alloc_pages(_node) failed for uio_desc.order = %d, gfpbpa_zone = %d\n", cpid, uio_desc.order, gfpbpa_zone));
	    up(&semlock);
	    kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA)): semaphore released\n"))
            return(-CMEM_RCC_GFP);
          }	
	}
	else
	{
	  kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): allocating memory from any zone\n", cpid));
	  if(uio_desc.type == TYPE_NUMA)
	  {
    	    kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): Allocate anywhere. uio_desc.numa_id = %d, uio_desc.order = %d\n", cpid, uio_desc.numa_id, uio_desc.order));
	    page_ptr = alloc_pages_node(uio_desc.numa_id, GFP_ATOMIC, uio_desc.order);
	  }
	  else  	
	  {  
	    kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): Allocate anywhere. uio_desc.order = %d\n", cpid, uio_desc.order));
	    page_ptr = alloc_pages(GFP_ATOMIC, uio_desc.order);
	  }
	  if (!page_ptr)
	  {
            kerror(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): ERROR: alloc_pages_node failed\n", cpid));
            buffer_table[tnum].used = 0;  // No longer required
            pptr->buffer[tnum] = 0;
	    kerror(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): ERROR: alloc_pages(_node) failed for uio_desc.order = %d, gfpbpa_zone = %d\n", cpid, uio_desc.order, gfpbpa_zone));
	    up(&semlock);
	    kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA)): semaphore released\n"))
            return(-CMEM_RCC_GFP);
          }
	}

	numa_address = page_address(page_ptr);
        kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): numa_address = 0x%p\n", cpid, numa_address));
	uio_desc.kaddr = (unsigned long) numa_address;
        kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): uio_desc.kadd = 0x%016lx\n", cpid, uio_desc.kaddr));

        //////uio_desc.paddr = virt_to_bus((void *) numa_address);
        uio_desc.paddr = virt_to_phys((void *) numa_address);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): physical address = 0x%016lx\n", cpid, uio_desc.paddr));

        if(gfpbpa_zone && ((uio_desc.paddr >> 32) != 0))
	{
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA), %d): ERROR: physical address (0x%016lx) is not below the 4GB limit\n", cpid, uio_desc.paddr));
          __free_pages(page_ptr, uio_desc.order);
          buffer_table[tnum].used = 0;  // No longer required
          pptr->buffer[tnum] = 0;
	  up(&semlock);
	  kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET(NUMA)): semaphore released\n"))
          return(-CMEM_RCC_ABOVE4G);
        }

	uio_desc.size = PAGE_SIZE * (1 << uio_desc.order);
        buffer_table[tnum].order  = uio_desc.order;
      }     
      
      else if (uio_desc.type == TYPE_GFPBPA || uio_desc.type == TYPE_NUMABPA)
      {
        pagecount = (int)((uio_desc.size - 1) / PAGE_SIZE + 1); // pages
        buffer_table[tnum].order = pagecount;  //MJ note: for the BPA variant we abuse "order". It is not the "order" but the "number of pages"
        kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): requested number of BPA pages = %d\n", cpid, pagecount));
	
	if(uio_desc.type == TYPE_GFPBPA)
  	  uio_desc.kaddr = (u_long)membpa_alloc_pages(pagecount, 0, GFP_KERNEL, uio_desc.type, 0);
	else 
	{
  	  uio_desc.kaddr = (u_long)membpa_alloc_pages(pagecount, 0, GFP_KERNEL, uio_desc.type, uio_desc.numa_id);
	  kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): Pool: NUMA, numa_id = %d\n", cpid, uio_desc.numa_id));
	}
	if (!uio_desc.kaddr)
	{
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): ERROR: membpa_alloc_pages returns 0\n", cpid));
          kerror(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): ERROR: uio_desc.handle = %d\n", cpid, uio_desc.handle));
          buffer_table[tnum].used = 0;  // No longer required
          pptr->buffer[tnum] = 0;
	  up(&semlock);
	  kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): semaphore released\n"))
          return(-CMEM_RCC_BPA);
	}

        //////uio_desc.paddr = virt_to_bus((void *) uio_desc.kaddr);
        uio_desc.paddr = virt_to_phys((void *) uio_desc.kaddr);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET,GFPBPA, %d): uio_desc.kaddr = 0x%016lx\n", cpid, uio_desc.kaddr));
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET,GFPBPA, %d): uio_desc.paddr = 0x%016lx\n", cpid, uio_desc.paddr));
 
        // Reserve all pages to make them remapable
 	
        uio_desc.size = PAGE_SIZE * pagecount;
	if (uio_desc.kaddr == 0)
	{
	  kerror(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): ERROR: error on buffer allocation\n", cpid));
          buffer_table[tnum].used = 0;  //Not required any more
          pptr->buffer[tnum] = 0;
	  up(&semlock);
	  kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): semaphore released\n"))
	  return(-CMEM_RCC_BPA);
	}
      }
      else
      {
	kerror(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): ERROR: uio_desc.type %d is not defined\n", cpid, uio_desc.type));
	break;
      }

      // Complete the entry in the buffer table
      buffer_table[tnum].size   = uio_desc.size;
      buffer_table[tnum].paddr  = uio_desc.paddr;
      buffer_table[tnum].kaddr  = uio_desc.kaddr;
      buffer_table[tnum].pid    = current->pid;
      buffer_table[tnum].type   = uio_desc.type;
      buffer_table[tnum].numa_id= uio_desc.numa_id;
      buffer_table[tnum].locked = 0;
      strcpy(buffer_table[tnum].name, uio_desc.name);

      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): PAGE_SIZE       = 0x%08x\n", cpid, (u_int)PAGE_SIZE));
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): buffer_table[%d].kaddr = 0x%016lx\n", cpid, tnum, buffer_table[tnum].kaddr));
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): buffer_table[%d].paddr = 0x%016lx\n", cpid, tnum, buffer_table[tnum].paddr));
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): buffer_table[%d].size  = 0x%016lx\n", cpid, tnum, buffer_table[tnum].size));
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): buffer_table[%d].name  = %s\n", cpid, tnum, buffer_table[tnum].name));
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): buffer_table[%d].order = 0x%08x\n", cpid, tnum, (u_int)buffer_table[tnum].order));
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): buffer_table[%d].type  = 0x%08x\n", cpid, tnum, (u_int)buffer_table[tnum].type));
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): uio_desc.handle = %d\n", cpid, (u_int)uio_desc.handle));

      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): buffer_table[%d].pid = %d\n", cpid, tnum, buffer_table[tnum].pid));

      if (copy_to_user((void *)arg, &uio_desc, sizeof(cmem_rcc_t)) != 0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): ERROR: error in from copy_to_user\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): semaphore released\n"))
        return(-CMEM_RCC_CTU);
      }

      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET, %d): done\n", cpid));
      break;
    }

    case CMEM_RCC_FREE:
    {
      u_int handle;
      int ret;
 
      if (copy_from_user(&handle, (void *)arg, sizeof(int)) != 0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_FREE, %d): ERROR: error in from copy_from_user\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): semaphore released\n"))
        return(-CMEM_RCC_CFU);
      }

      kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE, %d): handle = %d\n", cpid, handle));
      // Check if the handle makes sense
      if (buffer_table[handle].used == 0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_FREE, %d): ERROR: Invalid handle\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): semaphore released\n"))
        return(-CMEM_RCC_ILLHAND);
      }
            
      buffer_table[handle].used = 0;

      if (buffer_table[handle].type == TYPE_GFP || buffer_table[handle].type == TYPE_NUMA)
      {
	// free the area
	free_pages(buffer_table[handle].kaddr, buffer_table[handle].order);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE, %d): pages freed\n", cpid));
      }
      else //TYPE_GFPBA and TYPE_NUMABPA
      {
        ret = membpa_free_pages((void *)buffer_table[handle].kaddr, buffer_table[handle].type, buffer_table[handle].numa_id);
	if (ret)
	{
  	  kerror(("cmem_rcc(cmem_rcc_release): ERROR: Error code %d received from membpa_free_pages\n", ret));
	}
        kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE, %d): Memory freed @ address 0x%016lx for numa_id = %d\n", cpid, buffer_table[handle].kaddr, buffer_table[handle].numa_id));
      }

      //MJ 8.3.2023: 
      //   pptr is a pointer to a data structure of type private_stuff that belongs to a specific user process
      //   Each user application has its own "private_stuff".
      //   It is possible that CMEM_RCC_FREE, via CMEM_SegmentUnlockAndFree, gets called from a process that did not create (and therefore own)
      //   the buffer that will be freed. We must only set buffer[handle] to zero for buffers that have been allocated by the same process that de-allocates them.
      if (buffer_table[handle].pid == cpid)
      {
        kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE, %d): Removing handle %d from the list of handles of this process\n", cpid, handle));
        pptr->buffer[handle] = 0;
      }
      else
        kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE, %d): Not removing handle %d. Buffer was allocated by PID %d\n", cpid, handle, buffer_table[handle].pid));
      
      // Delete the entry in the buffer table
      buffer_table[handle].paddr  = 0;
      buffer_table[handle].locked = 0;
      buffer_table[handle].pid    = 0;
      buffer_table[handle].kaddr  = 0;
      buffer_table[handle].type   = 0;
      buffer_table[handle].order  = 0;
      buffer_table[handle].size   = 0;  //This enables the entry to be re-used
      
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE, %d): done\n", cpid));
      break;
    }

    case CMEM_RCC_LOCK:
    {
      u_int handle;

      if (copy_from_user(&handle, (void *)arg, sizeof(int)) !=0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_LOCK, %d): ERROR: error in from copy_from_user\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_LOCK): semaphore released\n"))
        return(-CMEM_RCC_CFU);
      }

      kdebug(("cmem_rcc(ioctl,CMEM_RCC_LOCK, %d): handle = 0x%08x\n", cpid, handle));

      // Check if the handle makes sense
      if (buffer_table[handle].used == 0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_LOCK, %d): ERROR: Invalid handle\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_LOCK): semaphore released\n"))
        return(-CMEM_RCC_ILLHAND);
      }

      buffer_table[handle].locked = 1;

      kdebug(("cmem_rcc(ioctl,CMEM_RCC_LOCK, %d): done\n", cpid));
      break;
    }

    case CMEM_RCC_UNLOCK:
    {
      u_int handle;

      if (copy_from_user(&handle, (void *)arg, sizeof(int)) !=0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_UNLOCK, %d): ERROR: error in from copy_from_user\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_UNLOCK): semaphore released\n"))
        return(-CMEM_RCC_CFU);
      }

      kdebug(("cmem_rcc(ioctl,CMEM_RCC_UNLOCK, %d): handle = 0x%08x\n", cpid, handle));

      // Check if the handle makes sense
      if (buffer_table[handle].used == 0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_UNLOCK, %d): ERROR: Invalid handle\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_UNLOCK): semaphore released\n"))
        return(-CMEM_RCC_ILLHAND);
      }

      buffer_table[handle].locked = 0;

      kdebug(("cmem_rcc(ioctl,CMEM_RCC_UNLOCK, %d): done\n", cpid));
      break;
    }

    case CMEM_RCC_GETPARAMS:
    {
      cmem_rcc_t uio_desc;

      if (copy_from_user(&uio_desc, (void *)arg, sizeof(cmem_rcc_t)) != 0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS, %d): ERROR: error in from copy_from_user\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): semaphore released\n"))
        return(-CMEM_RCC_CFU);
      }
      
      // Check if the handle makes sense
      if (uio_desc.handle < 0 || uio_desc.handle > MAX_BUFFS)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS, %d): ERROR: handle %d is out of range\n", cpid, uio_desc.handle));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): semaphore released\n"))
        return(-CMEM_RCC_ILLHAND);
      }      
      
      if (buffer_table[uio_desc.handle].used == 0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS, %d): ERROR: Invalid handle %d\n", cpid, uio_desc.handle));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): semaphore released\n"))
        return(-CMEM_RCC_ILLHAND);
      }
      
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS, %d): called for handle %d\n", cpid, uio_desc.handle));
      uio_desc.paddr   = buffer_table[uio_desc.handle].paddr;
      uio_desc.uaddr   = buffer_table[uio_desc.handle].uaddr;
      uio_desc.kaddr   = buffer_table[uio_desc.handle].kaddr;
      uio_desc.size    = buffer_table[uio_desc.handle].size;
      
      if (!uio_desc.size)  //MJ: just for debugging
      {
	kerror(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): ERROR: SIZE IS ZERO\n"));
      }
      
      uio_desc.order   = buffer_table[uio_desc.handle].order;
      uio_desc.locked  = buffer_table[uio_desc.handle].locked;
      uio_desc.type    = buffer_table[uio_desc.handle].type;
      uio_desc.numa_id = buffer_table[uio_desc.handle].numa_id;
      strcpy(uio_desc.name, buffer_table[uio_desc.handle].name);
      
      if (copy_to_user((void *)arg, &uio_desc, sizeof(cmem_rcc_t)) != 0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS, %d): ERROR: error in from copy_to_user\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): semaphore released\n"))
        return(-CMEM_RCC_CTU);
      }
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS, %d): done\n", cpid));
      break;
    }

    case CMEM_RCC_SETUADDR:
    {
      cmem_rcc_t uio_desc;

      if (copy_from_user(&uio_desc, (void *)arg, sizeof(cmem_rcc_t)) !=0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR, %d): ERROR: error in from copy_from_user\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): semaphore released\n"))
        return(-CMEM_RCC_CFU);
      }

      // Check if the handle makes sense
      if (buffer_table[uio_desc.handle].used == 0)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR, %d): ERROR: Invalid handle\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): semaphore released\n"))
        return(-CMEM_RCC_ILLHAND);
      }
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR, %d): called for handle %d\n", cpid, uio_desc.handle));
      kdebug(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR, %d): uaddr = 0x%016lx\n", cpid, uio_desc.uaddr));
      buffer_table[uio_desc.handle].uaddr = uio_desc.uaddr;

      kdebug(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR, %d): done\n", cpid));
      break;
    }

    case CMEM_RCC_DUMP:
    {
      char *buf;
      int len, loop;

      kdebug(("cmem_rcc(ioctl,CMEM_RCC_DUMP, %d): called\n", cpid));

      buf = (char *)kmalloc(TEXT_SIZE, GFP_KERNEL);
      if (buf == NULL)
      {
        kerror(("cmem_rcc(ioctl,CMEM_RCC_DUMP, %d): ERROR: error from kmalloc\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_DUMP): semaphore released\n"))
        return(-CMEM_RCC_KMALLOC);
      }

      len = 0;
      len += sprintf(buf + len, "Memory allocated by alloc_pages\n");
      len += sprintf(buf + len, "   PID |         Phys. address |               Size | Order | Locked | Name\n");
//MJ-SMP: protect this fragment (preferrably with a spinlock)
      for(loop = 0; loop < MAX_BUFFS; loop++)
      {
        if (buffer_table[loop].used && (buffer_table[loop].type == TYPE_GFP || buffer_table[loop].type == TYPE_NUMA))
        {
          len += sprintf(buf + len, "%6d |", buffer_table[loop].pid);
          len += sprintf(buf + len, "    0x%016lx |", buffer_table[loop].paddr);
          len += sprintf(buf + len, " 0x%016lx |", buffer_table[loop].size);
          len += sprintf(buf + len, "     %d |", buffer_table[loop].order);
          len += sprintf(buf + len, "    %s |", buffer_table[loop].locked ? "yes" : " no");
          len += sprintf(buf + len, " %s\n", buffer_table[loop].name);
        }
      }

      if(gfpbpa_size)
      {
	len += sprintf(buf + len, "Memory allocated by GFPBPA\n");
	len += sprintf(buf + len, "   PID |         Phys. address |               Size | Locked | Name\n");
	for(loop = 0; loop < MAX_BUFFS; loop++)
	{
          if (buffer_table[loop].used && (buffer_table[loop].type == TYPE_GFPBPA || buffer_table[loop].type == TYPE_NUMABPA))
          {
            len += sprintf(buf + len, "%6d |", buffer_table[loop].pid);
            len += sprintf(buf + len, "    0x%016lx |", buffer_table[loop].paddr);
            len += sprintf(buf + len, " 0x%016lx |", buffer_table[loop].size);
            len += sprintf(buf + len, "    %s |", buffer_table[loop].locked ? "yes" : " no");
            len += sprintf(buf + len, " %s\n", buffer_table[loop].name);
          }
	}
      }
//MJ-SMP: end of protected zone

      if (copy_to_user((void *)arg, buf, TEXT_SIZE * sizeof(char)) != 0)
      {
	kerror(("cmem_rcc(ioctl,CMEM_RCC_DUMP, %d): ERROR: error from copy_to_user\n", cpid));
	up(&semlock);
	kdebug(("cmem_rcc(ioctl,CMEM_RCC_DUMP): semaphore released\n"))
	return(-CMEM_RCC_CTU);
      }

      kfree(buf);
      break;
    }
    
    default:
    {
      kerror(("cmem_rcc(ioctl,default): ERROR: You should not be here\n"))
      kerror(("cmem_rcc(ioctl,default): ERROR: CMEM_RCC_GET       = 0x%016lx\n", CMEM_RCC_GET))
      kerror(("cmem_rcc(ioctl,default): ERROR: CMEM_RCC_FREE      = 0x%016lx\n", CMEM_RCC_FREE));
      kerror(("cmem_rcc(ioctl,default): ERROR: CMEM_RCC_LOCK      = 0x%016lx\n", CMEM_RCC_LOCK));
      kerror(("cmem_rcc(ioctl,default): ERROR: CMEM_RCC_UNLOCK    = 0x%016lx\n", CMEM_RCC_UNLOCK));
      kerror(("cmem_rcc(ioctl,default): ERROR: CMEM_RCC_GETPARAMS = 0x%016lx\n", CMEM_RCC_GETPARAMS));
      kerror(("cmem_rcc(ioctl,default): ERROR: CMEM_RCC_SETUADDR  = 0x%016lx\n", CMEM_RCC_SETUADDR));
      kerror(("cmem_rcc(ioctl,default): ERROR: CMEM_RCC_DUMP      = 0x%016lx\n", (u_long)CMEM_RCC_DUMP));
      kerror(("cmem_rcc(ioctl,default): ERROR: returning error -EINVAL\n"))
      return(-EINVAL);
    }
  }

  up(&semlock);
  kdebug(("cmem_rcc(ioctl): semaphore released\n"))
  return(0);
}


/******************************************************/
static void cmem_rcc_vmaOpen(struct vm_area_struct *vma)
/******************************************************/
{
  kdebug(("cmem_rcc_vmaOpen: Called\n"));
}


/*******************************************************/
static void cmem_rcc_vmaClose(struct vm_area_struct *vma)
/*******************************************************/
{
  kdebug(("cmem_rcc(cmem_rcc_vmaClose): Virtual address  = 0x%016lx\n", (u_long)vma->vm_start));
  kdebug(("cmem_rcc(cmem_rcc_vmaClose): mmap released\n"));
}


/*************************************************************************************/
static int cmem_rcc_mmap_remap_pfn_range(struct file *file, struct vm_area_struct *vma)
/*************************************************************************************/
{
  u_long startaddress, size, lastaddress;
  int loop, number_of_numa_or_virtual_zones = max(numa_zones, virtual_zones), valid_mappings = 0;
  bool mmap_possible_error_flag[MAX_NUMA_ZONES];
    
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): called\n", current->pid));
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,3,0)
  vma->vm_flags |= VM_DONTEXPAND;
  vma->vm_flags |= VM_DONTDUMP;
  vma->vm_flags |= VM_DONTEXPAND;
#else
  vm_flags_set(vma, VM_DONTEXPAND);
  vm_flags_set(vma, VM_DONTDUMP);
  vm_flags_set(vma, VM_DONTEXPAND);
#endif
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): vma->vm_end    = 0x%016lx\n", current->pid, (u_long)vma->vm_end));
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): vma->vm_start  = 0x%016lx\n", current->pid, (u_long)vma->vm_start));
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): vma->vm_offset = 0x%016lx\n", current->pid, (u_long)vma->vm_pgoff << PAGE_SHIFT));
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): vma->vm_flags  = 0x%08x\n", current->pid, (u_int)vma->vm_flags));

  size = vma->vm_end - vma->vm_start;
  startaddress = vma->vm_pgoff << PAGE_SHIFT;
  lastaddress = startaddress + size - 1;
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): startaddress   = 0x%016lx\n", current->pid, startaddress));
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): size           = 0x%016lx\n", current->pid, size));
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): lastaddress    = 0x%016lx\n", current->pid, lastaddress));
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): valid_mappings = %d\n", current->pid, valid_mappings));
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): number_of_numa_or_virtual_zones = %d\n", current->pid, number_of_numa_or_virtual_zones));

  // check whether area is contained in zone
  for (loop = 0; loop < number_of_numa_or_virtual_zones; loop++)
  {
    mmap_possible_error_flag[loop] = false;
    kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): buffer %d first address = 0x%016lx\n", current->pid, loop, gfpbpa_base[loop]));
    kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): buffer %d size          = 0x%016lx\n", current->pid, loop, gfpbpa_size * 1024 * 1024));
    
    if (startaddress < gfpbpa_base[loop])
    {
      kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): Condition 1\n", current->pid));
      mmap_possible_error_flag[loop] = true;
    }

    if (lastaddress > gfpbpa_base[loop] + gfpbpa_size * 1024 * 1024)
    {
      kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): Condition 2\n", current->pid));
      mmap_possible_error_flag[loop] = true;
    }
  }
  
  for (loop = 0; loop < number_of_numa_or_virtual_zones; loop++)
  {
    kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): mmap_possible_error_flag[%d] = %d\n", current->pid, loop, mmap_possible_error_flag[loop]));
    if (!mmap_possible_error_flag[loop])
      valid_mappings++;
  }
  
  if (valid_mappings != 1)
  {
    kerror(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): ERROR: Invalid offset and / or size !!!  valid_mappings = %d\n", current->pid, valid_mappings));
    return(-1);
  }
  
  if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size, vma->vm_page_prot))
  {
    kerror(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): ERROR: function remap_page_range failed \n", current->pid));
    return(-CMEM_RCC_MMAP);
  }
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): vma->vm_start(2) = 0x%016lx\n", current->pid, (u_long)vma->vm_start));

  vma->vm_ops = &cmem_rcc_vm_ops;
  kdebug(("cmem_rcc(cmem_rcc_mmap_remap_pfn_range, %d): cmem_rcc_mmap done\n", current->pid));
  return(0);
}


/****************************************************************************/
static int cmem_rcc_mmap_nopage(struct file *filp, struct vm_area_struct *vma)
/****************************************************************************/
{
  u_long startaddress, size, lastaddress;
  int loop, number_of_numa_or_virtual_zones = max(numa_zones, virtual_zones), valid_mappings = 0;
  bool mmap_possible_error_flag[MAX_NUMA_ZONES];

  kdebug(("cmem_rcc(cmem_rcc_mmap_nopage): cmem_rcc_mmap_nopage called\n"));

  size = vma->vm_end - vma->vm_start;
  startaddress = vma->vm_pgoff << PAGE_SHIFT;
  lastaddress = startaddress + size - 1;
  kdebug(("cmem_rcc(cmem_rcc_mmap_nopage): startaddress = 0x%016lx\n", startaddress));
  kdebug(("cmem_rcc(cmem_rcc_mmap_nopage): size = 0x%016lx\n", size));
  kdebug(("cmem_rcc(cmem_rcc_mmap_nopage): lastaddress = 0x%016lx\n", lastaddress));

  // check whether area is contained in zone
  for (loop = 0; loop < number_of_numa_or_virtual_zones; loop++)
  {
      mmap_possible_error_flag[loop] = false;
      kdebug(("cmem_rcc(cmem_rcc_mmap_nopage): buffer %d first address = 0x%016lx\n", loop, gfpbpa_base[loop]));
      kdebug(("cmem_rcc(cmem_rcc_mmap_nopage): buffer %d size = 0x%016lx\n", loop, gfpbpa_size * 1024 * 1024));
      
      if (startaddress < gfpbpa_base[loop])
        mmap_possible_error_flag[loop] = true;

      if (lastaddress > gfpbpa_base[loop] + gfpbpa_size * 1024 * 1024)
        mmap_possible_error_flag[loop] = true;
  }
  
  for (loop = 0; loop < number_of_numa_or_virtual_zones; loop++)
  {
    if (!mmap_possible_error_flag[loop])
      valid_mappings++;
  }
  
  if (valid_mappings != 1)
  {
    kerror(("cmem_rcc(cmem_rcc_mmap_nopage): ERROR: Invalid offset and / or size !!!\n"));
    return(-CMEM_RCC_MMAP);
  }

  vma->vm_ops = &cmem_rcc_vm_ops;
  cmem_rcc_vmaOpen(vma);
  return 0;
}


/**************************************************************************/
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)
  static int cmem_rcc_nopage(struct vm_area_struct *vma, struct vm_fault *vmf)
#else
  static vm_fault_t cmem_rcc_nopage(struct vm_fault *vmf)
#endif
/**************************************************************************/
{
  struct page *pageptr;
  
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)
  unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
  unsigned long physaddr = (unsigned long) vmf->virtual_address - vma->vm_start + offset;
  unsigned long pageframe = physaddr >> PAGE_SHIFT;
#else
  struct vm_area_struct *vma = vmf->vma;
  unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
  unsigned long physaddr = (unsigned long) vmf->address - vma->vm_start + offset;
  unsigned long pageframe = physaddr >> PAGE_SHIFT;
#endif

  kdebug(("cmem_rcc(cmem_rcc_nopage): called, offset %lx phys %lx pfn %lx\n", offset, physaddr, pageframe));
  if (!pfn_valid(pageframe))
  {
    kerror(("cmem_rcc(cmem_rcc_nopage): ERROR: Error received from pfn_valid\n"));
    return VM_FAULT_SIGBUS;
  }

  pageptr = pfn_to_page(pageframe);
  get_page(pageptr);
  vmf->page = pageptr;

  return 0;
}


/**********************************************************************************************************/
static ssize_t cmem_rcc_proc_write(struct file *file, const char *buffer, size_t count, loff_t *startOffset)
/**********************************************************************************************************/
{
  int len, loop, ret;
  char value[100];

  kdebug(("cmem_rcc(cmem_rcc_proc_write): cmem_rcc_proc_write called\n"));

  if(count > 99)
    len = 99;
  else
    len = count;

  if (copy_from_user(value, buffer, len))
  {
    kerror(("cmem_rcc(cmem_rcc_proc_write): ERROR: error from copy_from_user\n"));
    return(-EFAULT);
  }

  kdebug(("cmem_rcc(cmem_rcc_proc_write): len = %d\n", len));
  value[len - 1] = '\0';
  kdebug(("cmem_rcc(cmem_rcc_proc_write): text passed = %s\n", value));

  if (!strcmp(value, "debug"))
  {
    debug = 1;
    kdebug(("cmem_rcc(cmem_rcc_proc_write): debugging enabled\n"));
  }

  if (!strcmp(value, "nodebug"))
  {
    kdebug(("cmem_rcc(cmem_rcc_proc_write): debugging disabled\n"));
    debug = 0;
  }

  if (!strcmp(value, "elog"))
  {
    kdebug(("cmem_rcc(cmem_rcc_proc_write): error logging enabled\n"))
    errorlog = 1;
  }

  if (!strcmp(value, "noelog"))
  {
    kdebug(("cmem_rcc(cmem_rcc_proc_write): error logging disabled\n"))
    errorlog = 0;
  }

  if (!strcmp(value, "freelock"))
  {
    kdebug(("cmem_rcc(cmem_rcc_proc_write): releasing all locked segments\n"));

  if (down_interruptible(&semlock))
  {  
    kerror(("cmem_rcc(cmem_rcc_release, %d): down_interruptible was interrupted\n", current->pid));
    return(-EINTR);
  }
  kdebug(("cmem_rcc(cmem_rcc_release): semaphore taken\n"));
    
    for(loop = 0; loop < MAX_BUFFS; loop++)
    {
      if (buffer_table[loop].used && buffer_table[loop].locked)
      {
        kdebug(("cmem_rcc(cmem_rcc_proc_write): releasing locked buffer: type=%d  paddr=0x%016lx  size=0x%016lx  name=%s numa_id = %d\n", buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name, buffer_table[loop].numa_id));
	if (buffer_table[loop].type == TYPE_GFP || buffer_table[loop].type == TYPE_NUMA)
	{
 	  free_pages(buffer_table[loop].kaddr, buffer_table[loop].order);  // free the area
	}
        else  //TYPE_GFPBPAand TYPE_NUMABPA
	{
	  ret = membpa_free_pages((void *)buffer_table[loop].kaddr, buffer_table[loop].type, buffer_table[loop].numa_id); //unreserve pages
	  if (ret)
	  {
  	    kerror(("cmem_rcc(cmem_rcc_release): ERROR: Error code %d received from membpa_free_pages\n", ret));
	  }
        }

	// clear the entry in the buffer table
	buffer_table[loop].paddr = 0;
	buffer_table[loop].kaddr = 0;
	buffer_table[loop].size  = 0;
	buffer_table[loop].type  = 0;
	buffer_table[loop].pid   = 0;
	buffer_table[loop].order = 0;
	buffer_table[loop].used  = 0;
      }
    }
    up(&semlock);
    kdebug(("cmem_rcc(cmem_rcc_release): semaphore released\n"))
  }

  return len;
}


/*****************************************************/
int cmem_rcc_proc_show(struct seq_file *sfile, void *p)
/*****************************************************/
{
  int loop, zoneloop;

  kdebug(("cmem_rcc(cmem_rcc_proc_show): Creating text....\n"));

  seq_printf(sfile, "\n");
  //seq_printf(sfile, "CMEM RCC driver (ALMA9, virt_to_phys) for TDAQ release %s (based on tag %s, cmem_rcc.c revision %s). V5semaphore\n", RELEASE_NAME, CVSTAG, CMEM_RCC_TAG);
  seq_printf(sfile, "CMEM RCC driver (ALMA9, virt_to_phys) for FELIX release 4.15\n");

  seq_printf(sfile, "\nThe driver was loaded with these parameters:\n");
  seq_printf(sfile, "gfpbpa_size    = %d\n", (int)gfpbpa_size);
  seq_printf(sfile, "gfpbpa_quantum = %d\n", gfpbpa_quantum);
  seq_printf(sfile, "gfpbpa_zone    = %d\n", gfpbpa_zone);
  if (virtual_zones > 1)
    seq_printf(sfile, "virtual_zones    = %d\n", virtual_zones);
  else
    seq_printf(sfile, "numa_zones     = %d\n", numa_zones);

  seq_printf(sfile, "\nalloc_pages and alloc_pages_node\n");
  seq_printf(sfile, "   PID | Handle |         Phys. address |               Size | Locked | Order | Type | Name\n");
  for(loop = 0; loop < MAX_BUFFS; loop++)
  {
    if (buffer_table[loop].used && (buffer_table[loop].type == TYPE_GFP || buffer_table[loop].type == TYPE_NUMA))
    {
      seq_printf(sfile, "%6d |", buffer_table[loop].pid);
      seq_printf(sfile, "%7d |", loop);
      seq_printf(sfile, "    0x%016lx |", buffer_table[loop].paddr);
      seq_printf(sfile, " 0x%016lx |", buffer_table[loop].size);
      seq_printf(sfile, "    %s |", buffer_table[loop].locked ? "yes" : " no");
      seq_printf(sfile, "    %2d |", buffer_table[loop].order);
      seq_printf(sfile, "    %d |", buffer_table[loop].type);
      seq_printf(sfile, " %s\n", buffer_table[loop].name);
    }
  }

  if (gfpbpa_size)
  {        
    seq_printf(sfile, "\n");

    for(zoneloop = 0; zoneloop < number_zones; zoneloop++)
    {
      if (type_zones)
        seq_printf(sfile, "GFPBPA (NUMA = %d, size = %lu MB, base = 0x%016lx)\n", zoneloop, gfpbpa_size, gfpbpa_base[zoneloop]);
      else
        seq_printf(sfile, "GFPBPA (Virtual = %d, size = %lu MB, base = 0x%016lx)\n", zoneloop, gfpbpa_size, gfpbpa_base[zoneloop]);
    }

    seq_printf(sfile, "   PID | Handle |         Phys. address |               Size | Locked | Type | Name\n");
    for(loop = 0; loop < MAX_BUFFS; loop++)
    {
      if (buffer_table[loop].used && (buffer_table[loop].type == TYPE_GFPBPA || buffer_table[loop].type == TYPE_NUMABPA))
      {
	seq_printf(sfile, "%6d |", buffer_table[loop].pid);
	seq_printf(sfile, "%7d |", loop);
	seq_printf(sfile, "    0x%016lx |", buffer_table[loop].paddr);
	seq_printf(sfile, " 0x%016lx |", buffer_table[loop].size);
	seq_printf(sfile, "    %s |", buffer_table[loop].locked ? "yes" : " no");
  	seq_printf(sfile, "    %d |", buffer_table[loop].type);
	seq_printf(sfile, " %s\n", buffer_table[loop].name);
      }
    }
  }

  seq_printf(sfile, " \n");
  seq_printf(sfile, "The command 'echo <action> > /proc/cmem_rcc', executed as root,\n");
  seq_printf(sfile, "allows you to interact with the driver. Possible actions are:\n");
  seq_printf(sfile, "debug    -> enable debugging\n");
  seq_printf(sfile, "nodebug  -> disable debugging\n");
  seq_printf(sfile, "elog     -> Log errors to /var/log/messages\n");
  seq_printf(sfile, "noelog   -> Do not log errors to /var/log/messages\n");
  seq_printf(sfile, "freelock -> release all locked segments\n");
  
  return(0);
}


/****************************************/
/* Service function (insourcing of BPA) */
/****************************************/



//MJ?? It may be possible to simplyfy the error handling in this function if we do not have to check the "btype"
/************************************************/
static int membpa_init2(int priority, u_int btype)
/************************************************/
{
  range_t *free_list;
  u_int loop;
  int number_of_numa_or_virtual_zones = max(numa_zones, virtual_zones);
  
  kdebug(("cmem_rcc(membpa_init2): called with priority = %d and btype = %d\n", priority, btype));
  
  if (btype != TYPE_GFPBPA && btype != TYPE_NUMABPA)
  {
    kerror(("cmem_rcc(membpa_init2): ERROR: btype = %d\n", btype));
    return(1);
  }

  for(loop = 0; loop < number_of_numa_or_virtual_zones; loop++)
  {
    if ((btype == TYPE_GFPBPA && gfpbpainit_level == 1) || (btype == TYPE_NUMABPA && gfpbpainit_level == 1))
    {
      free_list = (range_t *)kmalloc(sizeof(range_t), priority);
      if (free_list != NULL)
      {
	free_list->next = NULL;
	free_list->base = (void *)gfpbpa[loop];
	free_list->size = gfpbpa_size * 1024 * 1024;

	kdebug(("cmem_rcc(membpa_init2): gfpbpa_size * 1024 * 1024 = 0x%16lx\n", (gfpbpa_size * 1024 * 1024)));
	kdebug(("cmem_rcc(membpa_init2): free_list->size           = 0x%16lx\n", free_list->size));
	kdebug(("cmem_rcc(membpa_init2): free_list->base           = 0x%16lx\n", (u_long)free_list->base));
	kdebug(("cmem_rcc(membpa_init2): Initializing gfpbpafree_list\n"));
	
	gfpbpafree_list[loop] = free_list;

	kdebug(("cmem_rcc(membpa_init2): OK for NUMA %d\n", loop));
      }
      else
      {
        kerror(("cmem_rcc(membpa_init2): ERROR: free_list is NULL\n"));
        return 1;
      }
    }
    else
    {
      kerror(("cmem_rcc(membpa_init2): ERROR: init_level = %d\n", gfpbpainit_level));
      return 1;
    }
  }
  
  gfpbpainit_level = 2;
  return 0;
}


/********************************************************************************************/
static void *membpa_alloc_pages(int count, int align, int priority, u_int btype, u_int numaid)
/********************************************************************************************/
{
  range_t *range, **range_ptr, *new_range, *align_range;
  void *aligned_base = 0;
  int cpid;
  
  cpid = current->pid;
	    
  kdebug(("cmem_rcc(membpa_alloc_pages, %d): called with count = 0x%08x, align = 0x%08x, priority = 0x%08x, numaid = %d\n", cpid, count, align, priority, numaid));

  if ((btype == TYPE_GFPBPA && gfpbpainit_level < 2) || (btype == TYPE_NUMABPA && gfpbpainit_level < 2))  //MJ?? do I need to list the TYPEs?
  {
    if (membpa_init2(priority, btype))
    {
      kerror(("cmem_rcc(membpa_alloc_pages, %d): ERROR: error in membpa_init2\n", cpid));
      return(0);
    }
  }
  
  if (btype == TYPE_GFPBPA || btype == TYPE_NUMABPA)  //MJ?? do I need to list the TYPEs?
  {
    kdebug(("cmem_rcc(membpa_alloc_pages, %d): Memory will be allocated from the GFP pool at 0x%016lx\n", cpid, (u_long)&gfpbpafree_list[numaid]));  
    range_ptr = &gfpbpafree_list[numaid];
  }
  else
  {
    kerror(("cmem_rcc(membpa_alloc_pages, %d): ERROR: btype = %d\n", cpid, btype));
    return(0);
  }

  kdebug(("cmem_rcc(membpa_alloc_pages, %d): range_ptr is at 0x%016lx\n", cpid, (u_long)range_ptr));

  new_range   = NULL;
  align_range = NULL;

  if (align == 0)
    align = PAGE_SIZE;
  else
    align = align * PAGE_SIZE;

  kdebug(("cmem_rcc(membpa_alloc_pages, %d): align = %d\n", cpid, align));

  // Search a free block which is large enough, even with alignment.
  while (*range_ptr != NULL)
  {
    range = *range_ptr;
    aligned_base = (void *)((((u_long)range->base + align - 1) / align) * align);
    
    if (aligned_base + count * PAGE_SIZE <= range->base + range->size)
      break;

    range_ptr = &range->next;
  }

  if (*range_ptr == NULL)
  {
    kerror(("cmem_rcc(membpa_alloc_pages, %d): ERROR: *range_ptr is NULL\n", cpid));   //MJ: If you get this error you have most likely requested more memory than was available in the buffer allocated at boot time
    return(0);
  }

  range = *range_ptr;
  // When we have to align, the pages needed for alignment can be put back to the free pool.
  // We check here if we need a second range data structure later and allocate it now, so that we don't have to check for a failed kmalloc later.

  if (aligned_base - range->base + count * PAGE_SIZE < range->size)
  {
    new_range = (range_t *)kmalloc(sizeof(range_t), priority);
    if (new_range == NULL)
    {
      kerror(("cmem_rcc(membpa_alloc_pages, %d): ERROR: new_range is NULL\n", cpid));
      return(0);
    }
  }

  if (aligned_base != range->base)
  {
    align_range = (range_t *)kmalloc(sizeof(range_t), priority);
    if (align_range == NULL)
    {
      if (new_range != NULL)
        kfree(new_range);

      kerror(("cmem_rcc(membpa_alloc_pages, %d): ERROR: align_range is NULL\n", cpid));
      return(0);
    }

    align_range->base = range->base;
    align_range->size = aligned_base - range->base;
    range->base = aligned_base;
    range->size -= align_range->size;
    align_range->next = range;
    *range_ptr = align_range;
    range_ptr = &align_range->next;
  }

  if (new_range != NULL)
  {
    // Range is larger than needed, create a new list element for the used list and shrink the element in the free list.
    new_range->base = range->base;
    kdebug(("cmem_rcc(membpa_alloc_pages, %d): range is larger than needed new_range->base = 0x%016lx\n", cpid, (u_long)new_range->base));

    new_range->size = count * PAGE_SIZE;
    kdebug(("cmem_rcc(membpa_alloc_pages, %d): new_range->size = 0x%016lx\n", cpid, new_range->size));

    range->base = new_range->base + new_range->size;
    kdebug(("cmem_rcc(membpa_alloc_pages, %d): range->base = 0x%016lx\n", cpid, (u_long)range->base));

    range->size = range->size - new_range->size;
    kdebug(("cmem_rcc(membpa_alloc_pages, %d): range->size = 0x%016lx\n", cpid, range->size));
  }
  else
  {
    // Range fits perfectly, remove it from free list.
    *range_ptr = range->next;
    new_range = range;
    kdebug(("cmem_rcc(membpa_alloc_pages, %d): range fits. new_range->base = 0x%016lx\n", cpid, (u_long)new_range->base));
  }

  // Insert block into used list
  new_range->next = gfpbpaused_list[numaid];
  gfpbpaused_list[numaid] = new_range;

  return new_range->base;
}


/*****************************************************************/
static int membpa_free_pages(void *base, u_int btype, u_int numaid)
/*****************************************************************/
{
  range_t *prev, *next, *range, **range_ptr;
  int cpid;
  
  cpid = current->pid;
	    
  kdebug(("cmem_rcc(membpa_free_pages, %d): called with base = 0x%016lx and btype = %d and numaid = %d\n", cpid, (u_long)base, btype, numaid));

  if((btype != TYPE_GFPBPA) && (btype != TYPE_NUMABPA))
  {
    kerror(("cmem_rcc(membpa_free_pages, %d): ERROR: btype is %d\n", cpid, btype));
    return(-1);
  }

  // Search the block in the used list.
  range_ptr = &gfpbpaused_list[numaid];

  kdebug(("cmem_rcc(membpa_free_pages, %d): range_ptr is at 0x%016lx\n", cpid, (u_long)range_ptr));

  for (; *range_ptr != NULL; range_ptr = &(*range_ptr)->next)
  {
    if ((*range_ptr)->base == base)
       break;
  }

  if (*range_ptr == NULL)
  {
    kerror(("cmem_rcc(membpa_free_pages, %d): ERROR: not allocated!   base = 0x%016lx and btype = %d and numaid = %d\n", cpid, (u_long)base, btype, numaid));
    return(-2);
  }
  range = *range_ptr;

  // Remove range from the used list:
  *range_ptr = (*range_ptr)->next;

  // The free-list is sorted by address, search insertion point and insert block in free list.
  range_ptr = &gfpbpafree_list[numaid];  

  for (prev = NULL; *range_ptr != NULL; prev = *range_ptr, range_ptr = &(*range_ptr)->next)
    if ((*range_ptr)->base >= base)
      break;

  range->next = *range_ptr;
  *range_ptr  = range;

  // Concatenate free range with neighbors, if possible.
  // Try for upper neighbor (next in list) first, then for lower neighbor (predecessor in list).
  if (range->next != NULL && range->base + range->size == range->next->base)
  {
    next = range->next;
    range->size += range->next->size;
    range->next = next->next;
    kfree(next);
  }

  if (prev != NULL && prev->base + prev->size == range->base)
  {
    prev->size += prev->next->size;
    prev->next = range->next;
    kfree(range);
  }
  return(0);
}


/**************************/
static int gfpbpa_init(void)
/**************************/
{
  u_int sum, *page_pool, limit_32, pcnt, block_found, block_start, chunks_required, pool_index, numaloop, loop, chunk_shift, chunk_mask;
  u_long paddr, kaddr;
  struct page *numapage;
  void *numa_address;
  int pleft, pright, countleft, countright, startIndex, nextIndex, count, cnt0, cnt;
  long npages;
  u_long pfn_base;

  number_zones = 1;
  type_zones = 0;     //0 = normal, 1 = NUMA
  
  if (gfpbpa_zone == 1) 
    kdebug(("cmem_rcc(gfpbpa_init): Only 32-bit addresses (gfpbpa_zone = 1)\n"));
   
  if (virtual_zones > 1) 
  {
    kdebug(("cmem_rcc(gfpbpa_init): %d virtual zones have been requested\n", virtual_zones));
    number_zones = virtual_zones;
  }

  if (numa_zones > 1) 
  {   
    kdebug(("cmem_rcc(gfpbpa_init): %d NUMA zones have been requested\n", numa_zones));
    number_zones = numa_zones;
    type_zones = 1;
  }
    
  if (virtual_zones > MAX_NUMA_ZONES) 
  {
    kerror(("cmem_rcc(gfpbpa_init): ERROR: virtual_zones > MAX_NUMA_ZONES\n"));
    return(1);
  }

  if (numa_zones > MAX_NUMA_ZONES) 
  {   
    kerror(("cmem_rcc(gfpbpa_init): ERROR: nume_zones > MAX_NUMA_ZONES\n"));
    return(1);

  }
  
  if (numa_zones > 1 && virtual_zones > 1) 
  {
    kerror(("cmem_rcc(gfpbpa_init): ERROR: You cannot combine NUMA and virtual zones\n"));
    return(1);
  }  

  for(numaloop = 0; numaloop < number_zones; numaloop++)
  {
    kdebug(("cmem_rcc(gfpbpa_init): Processing zone %d of %d\n", numaloop + 1, number_zones));
    gfpbpa_base[numaloop]     = 0;
    gfpbpa[numaloop]          = 0;
    gfpbpafree_list[numaloop] = NULL;
    gfpbpaused_list[numaloop] = NULL;
    pcnt                      = 0;
    block_found               = 0;
    block_start               = 0;

    if (gfpbpa_quantum != 1 && gfpbpa_quantum != 2 && gfpbpa_quantum != 4 && gfpbpa_quantum != 8)
    {
      kerror(("cmem_rcc(gfpbpa_init): ERROR: gfpbpa_quantum is not 1, 2, 4 or 8\n"));
      return(1);
    }

    if (gfpbpa_size % gfpbpa_quantum)
    {
      kerror(("cmem_rcc(gfpbpa_init): ERROR: gfpbpa_size is not a multiple of gfpbpa_quantum\n"));
      return(1);
    }

    page_pool = (u_int *)vmalloc(MAX_GFPBPA_SIZE * sizeof(u_int)); //support at most 256 GB of RAM
    if (page_pool == NULL)
    {
      kerror(("cmem_rcc(gfpbpa_init): ERROR: page_pool is NULL\n"));
      return(1);
    }

    for (loop = 0; loop < MAX_GFPBPA_SIZE; loop++)
      page_pool[loop] = 0;                               //Set all pool entries to "no memory allocated"

    if      (gfpbpa_quantum == 1) {chunk_shift = 20; chunk_mask = 0x0fffff;}
    else if (gfpbpa_quantum == 2) {chunk_shift = 21; chunk_mask = 0x1fffff;}
    else if (gfpbpa_quantum == 4) {chunk_shift = 22; chunk_mask = 0x3fffff;}
    else                          {chunk_shift = 23; chunk_mask = 0x7fffff;}
    
    kdebug(("cmem_rcc(gfpbpa_init): gfpbpa_quantum = %d\n", gfpbpa_quantum));
    kdebug(("cmem_rcc(gfpbpa_init): chunk_shift    = %d\n", chunk_shift));
    chunks_required = gfpbpa_size / gfpbpa_quantum;
    kdebug(("cmem_rcc(gfpbpa_init): chunks_required = %d\n", chunks_required));

    limit_32 = (4096 / gfpbpa_quantum) - 1;
    kdebug(("cmem_rcc(gfpbpa_init): limit_32 = %d\n", limit_32));

    printk("cmem_rcc(gfpbpa_init): Trying to allocate a contiguous buffer of %lu MB in pieces of %d MB\n", gfpbpa_size, gfpbpa_quantum);
    gfpbpa_order = get_order(gfpbpa_quantum * 1024 * 1024);
    kdebug(("cmem_rcc(gfpbpa_init): This will be buffer number %d\n", numaloop));

    //Allocate pages until we have found a large enough contiguous buffer
    while(pcnt < MAX_GFPBPA_SIZE)
    {
      kdebug(("cmem_rcc(gfpbpa_init): Calling alloc_pages_node with order = %d and numa_id = %d\n", gfpbpa_order, numaloop));

      if (numa_zones > 1)
      {
        kdebug(("cmem_rcc(gfpbpa_init): NUMA zone\n"));
        numapage = alloc_pages_node(numaloop, GFP_ATOMIC, gfpbpa_order);
      }
      else
      {
        kdebug(("cmem_rcc(gfpbpa_init): virtual zone\n"));
        numapage = alloc_pages_node(0, GFP_ATOMIC, gfpbpa_order);   // no NUMA, but try to find virtual_zones contiguous memory areas
      }
      if (!numapage)
      {
        kerror(("cmem_rcc(gfpbpa_init): ERROR: error from call to alloc_pages_node\n"));
        break;
      }

      numa_address = page_address(numapage);
      kaddr = (unsigned long) numa_address;
      //////paddr = virt_to_bus((void *) numa_address);
      paddr = virt_to_phys((void *) numa_address);
      kdebug(("cmem_rcc(gfpbpa_init): Got buffer @ physical address 0x%016lx (pcnt = %d, numa_address = %p, numa node = %d, kaddr = 0x%016lx)\n", paddr, pcnt, numa_address, numaloop, kaddr));

      if (paddr & chunk_mask)
      {
        kerror(("cmem_rcc(gfpbpa_init): ERROR: paddr (0x%016lx) is not properly aligned to chunk_mask = 0x%08x\n", paddr, chunk_mask));
        kerror(("cmem_rcc(gfpbpa_init): ERROR: Potential memory leak. Memory may not be freed correctly. Reboot nachine!\n"));
        vfree((void *)page_pool);
        return(1);
      }

      pool_index = paddr >> chunk_shift;
      kdebug(("cmem_rcc(gfpbpa_init): pool_index = 0x%08x\n", pool_index));

      if (pool_index > (MAX_GFPBPA_SIZE - 2))  // checking here for pool_index not being larger than MAX_GFPBPA_SIZE - 2 makes later check on pright unnecesary
      {
        kerror(("cmem_rcc(gfpbpa_init): ERROR: pool_index (0x%08x) out of range. You seem to have more than 256 GB of RAM\n", pool_index));
        break;         // jump out of loop, already allocated pages will be freed as block_found is 0
      }
            
      if (page_pool[pool_index] == 0)
      {
        pleft = pool_index - 1;
        pright = pool_index + 1;
        if (pleft >= 0)
        {
          sum = page_pool[pleft] + 1;
        }
        else
        {
          pleft = 0;
          sum = 1;
        }
	
        // Maximum of pright = MAX_GFPBPA_SIZE - 1, due to check on ptr, no check needed
        sum = sum + page_pool[pright];
        countleft = page_pool[pleft];
        countright = page_pool[pright];
        page_pool[pool_index - countleft] = sum;
        page_pool[pool_index + countright] = sum;
        kdebug(("cmem_rcc(gfpbpa_init): pool_index = %d, countleft = %d, countright = %d, sum = %d\n", pool_index, countleft, countright, sum));
        
        page_pool[pool_index] = sum;  	// page_pool[pool_index] only needs to be set here for a later check on contiguity, it has already just been set to a value > 0 if countleft or countright = 0
	
        if (sum >= chunks_required)
        {
          if (gfpbpa_zone == 1)            // only 32-bit addresses allowed for block
          {
            if ((pool_index + countright) < limit_32)
            {
              block_found = 1;
              block_start = pool_index - countleft;
              kdebug(("cmem_rcc(gfpbpa_init): Range = %d to %d\n", block_start, pool_index + countright));
            }
          }
          else
          {
            block_found = 1;
            block_start = pool_index - countleft;
            kdebug(("cmem_rcc(gfpbpa_init): Range = %d to %d\n", block_start, pool_index + countright));
          }
        }
      }
      else                               // page was already allocated. Error -> terminate search, free allocated blocks
      {
        kerror(("cmem_rcc(gfpbpa_init): ERROR: same page allocated again. page index = %d\n", pool_index));
        break;                           // jump out of loop, already allocated pages will be freed as block_found is 0
      }
            
      if (block_found)
      {
        kdebug(("cmem_rcc(gfpbpa_init): Contiguous memory area found\n"));
        for (loop = block_start; loop < chunks_required; loop++)   // check that all page_pool entries are set for memory area found
        {
          if (page_pool[loop] == 0)
          {
            block_found = 0;
            kerror(("cmem_rcc(gfpbpa_init): ERROR: Not all entries in page_pool associated with contiguoues memory area set\n"));
            break;
          }
        }
        break;
      }
      pcnt++;
    }
        
    nextIndex  = 1;
    count      = 0;
    startIndex = 0;
    
    //Return the pages that we don't use
    if (block_found == 0)
    {
      kerror(("cmem_rcc(gfpbpa_init): ERROR: No buffer found. Returning all pages\n"));
      for(loop = 0; loop < MAX_GFPBPA_SIZE; loop++)   //return memory allocated so far
      {
        if (page_pool[loop])
        {
          paddr = (u_long)loop << chunk_shift;
          //////kaddr = (u_long)bus_to_virt(paddr);
          kaddr = (u_long)phys_to_virt(paddr);
          kdebug(("cmem_rcc(gfpbpa_init): Returning index(2) %d, paddr = 0x%016lx, kaddr = 0x%016lx)\n", loop, paddr, kaddr));
          free_pages(kaddr, gfpbpa_order);
	  //MJ: This block of code is just for nice formatting of the debug messages to /var/lg/messages
          if (loop == 0)
          {
            count = 1;  // to prevent false report
            continue;
          }
	  
          if (loop == nextIndex)
            count++;
          else
          {
            if (count > 0)
            {
              // count = 0 occurs only if for loop = 0 there was no page allocated
              kdebug(("cmem_rcc(gfpbpa_init): Returned contiguous area of %d pages / %d MByte from index %d, ending with index %d\n", count, count * gfpbpa_quantum, startIndex, nextIndex - 1));
            }
            count = 1; // start of next contiguous area
            startIndex = loop;
          }
          nextIndex = loop + 1;
	  //MJ: end of block of code
        }
      }
      kerror(("cmem_rcc(gfpbpa_init): ERROR: No buffer found\n"));
    }
    else
    {
      kdebug(("cmem_rcc(gfpbpa_init): Buffer found. Returning unused pages\n"));
      for(loop = 0; loop < MAX_GFPBPA_SIZE; loop++)   //return memory allocated so far
      {
        if ((page_pool[loop] > 0) && (loop < block_start || loop > (block_start + chunks_required - 1)))
        {
          paddr = (u_long)loop << chunk_shift;
          //////kaddr = (u_long)bus_to_virt(paddr);
          kaddr = (u_long)phys_to_virt(paddr);
          kdebug(("cmem_rcc(gfpbpa_init): Returning index(3) %d, paddr = 0x%016lx, kaddr = 0x%016lx)\n", loop, paddr, kaddr));
          free_pages(kaddr, gfpbpa_order);
	  //MJ: This block of code is just for nice formatting of the debug messages to /var/lg/messages
          if (loop == 0)
          {
            // to prevent false report
            count = 1;
            continue;
          }
          if (loop == nextIndex)
          {
            count++;
          }
          else
          {
            if (count > 0)
            {
              // count = 0 occurs only if for loop = 0 there was no page allocated
              kdebug(("cmem_rcc(gfpbpa_init): Returned unused contiguous area of %d pages / %d MByte from index %d, ending with index %d\n",  count, count * gfpbpa_quantum, startIndex, nextIndex - 1));
            }
            // start of next contiguous area
            count = 1;
            startIndex = loop;
          }
          nextIndex = loop + 1;
	  //MJ: end of block of code
        }
      }
    }
        
    if(block_found)
    {
      kdebug(("cmem_rcc(gfpbpa_init): Job done. Now copying used pages\n"));
      gfpbpa_num_pages[numaloop] = chunks_required;

      for(loop = 0; loop < chunks_required ; loop++)    //Copy the pages we want to keep to the global array
      {
        paddr = (u_long)(loop + block_start) << chunk_shift;
        //////kaddr = (u_long)bus_to_virt(paddr);
        kaddr = (u_long)phys_to_virt(paddr);
        gfpbpa_array[numaloop][loop] = kaddr;

        if (loop == 0)
          gfpbpa_base[numaloop] = paddr;
      }

      gfpbpa[numaloop] = gfpbpa_array[numaloop][0];
      kdebug(("cmem_rcc(gfpbpa_init): End of function. gfpbpa[%d] = 0x%016lx\n", numaloop, gfpbpa[numaloop]));
        
      // Set correct refcount for all pages (needed for nopage mmapping)
      npages = (gfpbpa_size * 1024 * 1024) / PAGE_SIZE;
      pfn_base = gfpbpa_base[numaloop] >> PAGE_SHIFT;
      kdebug(("cmem_rcc(gfpbpa_init): Set correct refcount for %ld pages starting at pfn 0x%lx\n", npages, pfn_base));
      cnt0 = page_count(pfn_to_page(pfn_base));  //See page chapter 8.1 of the book "Understanding the Linux Kernel" of 2005.
      SetPageReserved(pfn_to_page(pfn_base));
      for (loop = 1; loop < npages; loop++)
      {
         struct page *thepage = pfn_to_page(pfn_base + loop);
         SetPageReserved(thepage);                                   //This function will set the PG_reserved bit (Jos is not sure if it really needs to be done)
         cnt = page_count(thepage);
         if (cnt != cnt0)
         {
           get_page(thepage);
         }
	 
         cnt = page_count(thepage);  //MJ note: This line is needed because get_page(thepage) will change the refcounti (= value of cnt returned by page_count). 
         if (cnt != cnt0)
         {
           kerror(("cmem_rcc(gfpbpa_init): ERROR: should not occur: pfn %ld ref count %d  not %d\n", pfn_base + loop, cnt0, cnt));
         }
       }

      vfree((void *)page_pool);
    }
    else
    {
      gfpbpa_num_pages[numaloop] = 0;
      vfree((void *)page_pool);
      if (numaloop > 0)
      {
        // need to free here buffers allocated previously, if there are any
        int nloop;
        kdebug(("cmem_rcc(cmem_rcc_cleanup): releasing gfp_bpa pages\n"));
        for(nloop = 0; nloop < numaloop; nloop++)
        {
          kdebug(("Freeing %ld pages starting at kaddr = 0x%016lx for buffer %d\n", gfpbpa_num_pages[nloop], gfpbpa_array[nloop][0], nloop));
          for(loop = 0; loop < gfpbpa_num_pages[nloop]; loop++)
          {
            kdebug(("cmem_rcc(cmem_rcc_cleanup): releasing page with kaddr = 0x%016lx\n", gfpbpa_array[nloop][loop]));
            free_pages(gfpbpa_array[nloop][loop], gfpbpa_order);
          }
          gfpbpa_num_pages[nloop] = 0;
        }
      }
      kdebug(("cmem_rcc(gfpbpa_init): End of function\n"));
      return(1);
    }
  }
  return(0);
}
